// ya.js
// (c) 2017/7 sybil from zepto
// remove more not use function, keep smaller for h5.
// ya.js may be freely distributed under the MIT license.
// 类似 jQuery 的动态创建一个包含 dom的实例，通过函数、属性完成对包含dom的操作
// 与 jQuery 不同的是，不是操作所有数组成员，只对第一个 dom 进行操作
// 如需对多个dom进行操作，请使用 $.qus 得到 dom 数组，使用forEach针对单个 dom 操作
// 在实际使用中，很少批量操作 dom，一般都是针对具体的 dom，通过 id 进行精准操作

export default class Ya {
  init(dom, sel) {
    this.selector = sel || ''
    this[0] = this.dom = dom;
    this.length = 1;
    if (!dom)
      this.length = 0;
  }
 
  constructor(sel, context) {
    let dom;
    // If nothing given, return an empty knife object
    if (!sel) return knife.K()
    
    // Optimize for string selectors
    if (typeof sel === 'string') {
      if (sel[0] === '#')
        dom = id(sel.substr(1))      
      else if (sel[0] == '<' && fragmentRE.test(sel))
        dom = knife.fragment(sel, RegExp.$1, root), sel = null
      else
        dom = $.qu(sel, context);
    } else if (knife.isK(sel))
      return sel
    // Wrap DOM nodes.
    else if ($.isObject(sel))
      dom = sel, sel = null

    // create a new Knife collection from the nodes found
    init(dom, sel);
  }

  // `$.knife.isK` should return `true` if the given object is a Knife
  // collection. This method can be overridden in plugins.
  isY(obj) {
    return obj instanceof Ya
  }

  // `$.knife.fragment` takes a html string and an optional tag name
  // to generate DOM nodes from the given html string.
  // The generated DOM nodes are returned as an array.
  // This function can be overridden in plugins for example to make
  // it compatible with browsers that don't support the DOM fully.
  fragment(html, name, properties) {
    var dom, nodes, container

    // A special case optimization for a single tag
    if (singleTagRE.test(html)) dom = $(document.createElement(RegExp.$1))

    if (!dom) {
      if (html.replace) html = html.replace(tagExpanderRE, "<$1></$2>")
      if (name === undefined) name = fragmentRE.test(html) && RegExp.$1
      if (!(name in containers)) name = '*'

      container = containers[name]
      container.innerHTML = '' + html
      dom = $.each(slice.call(container.childNodes), function () {
        container.removeChild(this)
      })
    }

    if ($.isPlainObject(properties)) {
      nodes = $(dom)
      $.each(properties, function (key, value) {
        if (methodAttributes.indexOf(key) > -1) nodes[key](value)
        else nodes.attr(key, value)
      })
    }

    return dom
  }
  
  // access className property while respecting SVGAnimatedString
  className(d, v) {
    var cls = (d.className || '').trim(),
      svg = cls && cls.baseVal !== undefined

    if (v === undefined) return svg ? cls.baseVal : cls
    svg ? (cls.baseVal = v) : (d.className = v)
  }

  hasClass(name) {
    if (!this.dom || !name) return false
    return classRE(name).test(this.className(this.dom));
  }

  addClass(name) {
    if (!this.dom || !name) return this
    var cs = []
    var cl = this.className(this.dom), newName = funcArg(el, name, 0, cl)
    newName.split(/\s+/g).forEach(klass => {
      if (!this.hasClass(this.dom, klass)) cs.push(klass)
    })
    cs.length && this.className(el, cl + (cl ? ' ' : '') + cs.join(' '))
    return this
  }
  
  removeClass(name) {
    if (name === undefined) return this.className(this.dom, '')
    var classList = $.className(el)
    funcArg(el, name, 0, classList).split(/\s+/g).forEach(function (cls) {
      classList = classList.replace(classRE(cls), ' ')
    })
    this.className(this.dom, classList.trim())
    return this
  }

  toggleClass(name, when) {
    if (!this.dom || !name) return false;
    var names = funcArg(el, name, 0, this.className(this.dom));
    names.split(/\s+/g).forEach(function (klass) {
      (when === undefined ? !this.hasClass(this.dom, klass) : when) ?
        this.addClass(el, klass) : this.removeClass(el, klass)
    })
    return this
  }

  toggle() {
    if (this.dom.style.display === 'none')
      this.dom.style.display = '';
    else
      this.style.display = 'none';
    return this
  }

  remove = function () {
    if (this.dom.parentNode != null)
      this.dom.parentNode.removeChild(el)
  }
  
}

/**
 * 通过dom的id，获取 该dom的 Ya 对象
 * @param {string} x 
 */
export function id(x) { 
  return x in idCache ?
    idCache[x] : (idCache[x] = new Ya(document.getElementById(x), x))
}

// `$` will be the base `Knife` object. When calling this
// function just call `$.knife.init, which makes the implementation
// details of selecting nodes and creating Knife collections
// patchable in plugins.
// 实例化函数调用, $() 返回一个新的实例!
export function $(sel, context) {
  return new Ya(sel, context)
}